/* 
 * File:   XbeeIf.cpp
 * Author: minaev
 * 
 * Created on 25 Октябрь 2013 г., 8:46
 */

#include "XbeeIf.h"
#include "Xservice.h"
#include "Xmsg.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

XbeeIf::XbeeIf(Xcom* com) {
    this->com = com;
    this->maxSize = 256;
    this->timeoutWB = 100000;
    tmWaitByte = new XTimeOut(this->timeoutWB);
    isLiveThreadScan = 0;
    reqStopThread = 0;
}

XbeeIf::~XbeeIf() {
    reqStopThread = 1;
    while (isLiveThreadScan) {
        usleep(20);
    }
}

/**
 * scanint COM received bytes,
 * forming input packets and
 * add to inputBuf (regMsg)
 * @param obj
 * @return 
 */
void* XbeeIf::threadScan(void* obj) {
    XbeeIf* ths = (XbeeIf*) obj;

    ths->isLiveThreadScan = 1;
    static int mode = ITR_WAIT_7E;
    static unsigned int lenmsg;
    static int buf;
    static char* readbuf;

    while (ths->reqStopThread == 0) {
        switch (mode) {
            case ITR_WAIT_7E:
            {
                if (ths->waitByte() == 0x7E) {
                    mode = ITR_READ_LEN;
                }
                break;
            }
            case ITR_READ_LEN:
            {
                readbuf = ths->waitBuf(2);
                if (readbuf == NULL) {
                    //Xmsg::printf("error read len\n");
                    mode = ITR_WAIT_7E;
                } else {
                    lenmsg = ((unsigned char) readbuf[0] & 0xff) << 8;
                    lenmsg |= ((unsigned char) readbuf[1] & 0xff);
                    //Xmsg::printf("len msg: %i\n", lenmsg);
                    if (lenmsg > 0) {
                        mode = ITR_READ_PACK;
                    } else {
                        mode = ITR_WAIT_7E;
                    }
                }
                break;
            }
            case ITR_READ_PACK:
            {
                readbuf = ths->waitBuf(lenmsg);
                if (readbuf == NULL) {
                    //Xmsg::printf("ITR: timeout error in read pack\n");
                    mode = ITR_WAIT_7E;
                } else {
                    mode = ITR_READ_CRC;
                }
                break;
            }
            case ITR_READ_CRC:
            {
                for (int i = 0; i < lenmsg; i++) {
                    buf += (unsigned char) readbuf[i];
                }
                buf &= 0xFF;

                if (buf == ths->waitByte()) {
                    //Xmsg::printf("error in read crc");
                    mode = ITR_WAIT_7E;
                } else {
                    mode = ITR_REG_MSG;
                }
                break;
            }
            case ITR_REG_MSG:
            {
                ths->regMsg(readbuf, lenmsg);
                mode = ITR_WAIT_7E;
                break;
            }
        }
    }
    ths->isLiveThreadScan = 0;
    return NULL;
}

/**
 * start scan messages thread
 * @return 0 - success start thread, 1 - error
 */
int XbeeIf::startScanThread() {
    if (pthread_create(&(this->thScan),
            NULL,
            &XbeeIf::threadScan,
            (void*) this) != 0) {
        Xmsg::log("FATAL ERROR: Thread XbeeIf scan not started.");
        return 1;
    }
    return 0;
}

/**
 * wait input byte from COM.
 * @return input byte value, or -1 if timeout
 */
int XbeeIf::waitByte() {
    tmWaitByte->startTimeOut();
    static int avail = 0;
    static char chbuf;
    static int res;
    while ((avail = com->availBytes()) < 1 && !tmWaitByte->isTimeOut()) {
        usleep(50);
    }
    if (avail > 0) {
        res = com->xread(&chbuf, 1);
        if (res == 1) {
            res = (unsigned char) chbuf;
        } else {
            res = -1;
        }
    } else {
        res = -1;
    }
    return res;
}

/**
 * wait buffer from interface
 * @param len - count byte for reading
 * @return received message or NULL if timeout
 */
char* XbeeIf::waitBuf(int len) {
    static char* resbuf = NULL;
    static int buf;
    static int i;
    if (resbuf != NULL) {
        delete [] resbuf;
    }
    resbuf = new char[len];
    i = len;
    while (i > 0 && (buf = waitByte()) != -1) {
        resbuf[len - i] = (char) buf;
        i--;
    }

    if (buf == -1) {
        delete [] resbuf;
        resbuf = NULL;
    }
    return resbuf;
}

void XbeeIf::regMsg(const char* msg, int len) {
    if (inputMsgs.size()<this->maxSize) {
        static Xpack* pck = NULL;
        static char* storebuf;
        static int storelen;

        storelen = len + 4;
        storebuf = new char[storelen];
        for (int i = 3; i < storelen; i++) {
            storebuf[i] = msg[i - 3];
        }
        
        storebuf[0] = 0x7e;
        storebuf[1] = (len >> 8) & 0xff;
        storebuf[2] = len & 0xff;

        pck = new Xpack(storebuf);
        pck->setCRC();
        
        pthread_mutex_lock(&mutexAccessMsgs);
        inputMsgs.push(pck);
        pthread_mutex_unlock(&mutexAccessMsgs);
    } else {
        Xmsg::log("XbeeIf: input buffer overflow!");
    }

}

void XbeeIf::write(Xpack* pck) {
    com->xwrite(
    pck->toBytes(), 
            pck->getLength()+4);
}

Xpack* XbeeIf::read() {
    static Xpack* delNextRead = NULL;
    if (delNextRead != NULL) {
        delete delNextRead;
    }
    pthread_mutex_lock(&mutexAccessMsgs);
    if (inputMsgs.size() > 0) {
        delNextRead = inputMsgs.front();
        inputMsgs.pop();
    } else {
        delNextRead = NULL;
    }
    pthread_mutex_unlock(&mutexAccessMsgs);

    return delNextRead;
}




